/**
 * vim: tabstop=4
 * 
 * @license		http://www.gnu.org/licenses/gpl.html GPL Version 3
 * @author		Ian Moore <imooreyahoo@gmail.com>
 * @copyright	Copyright (c) 2011 Ian Moore
 * @version		$Id: mysql.js 68 2011-12-09 20:36:16Z imooreyahoo@gmail.com $
 *
 * This file is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this file. If not, see <http://www.gnu.org/licenses/>.
 */
// require("js/omv/NavigationPanel.js")
// require("js/omv/data/DataProxy.js")
// require("js/omv/form/FormPanel.js")
// require("js/omv/FormPanelExt.js")
// require("js/omv/grid/GridPanel.js")
// require("js/omv/grid/TBarGridPanel.js")
// require("js/omv/CfgObjectDialog.js")
// require("js/omv/form/SharedFolderComboBox.js")
// require("js/omv/form/PasswordField.js")
// require("js/omv/form/plugins/FieldInfo.js")


Ext.ns("OMV.Module.Services");

//Register the menu.
OMV.NavigationPanelMgr.registerMenu("services", "mysql", {
	text: "MySQL",
	icon: "images/mysql.png"
});

/**
 * @class OMV.Module.Services.MySQL
 * @derived OMV.FormPanelExt
 * 
 * Main configuration panel. First tab
 * 
 */
OMV.Module.Services.MySQL = function(config) {
	var initialConfig = {
			rpcService: "MySQL"
	};
	Ext.apply(initialConfig, config);
	OMV.Module.Services.MySQL.superclass.constructor.call(this, initialConfig);
};
Ext.extend(OMV.Module.Services.MySQL, OMV.FormPanelExt, {
	
	initComponent : function() {
		
		OMV.Module.Services.MySQL.superclass.initComponent.apply(this, arguments);
		
		// Update form fields
		this.on("load", function(t,r){

            // Fire select event on combo
            var mntent = this.findFormField("mntentref");
            var val = mntent.getValue();
            if(val) {

                var record = mntent.getStore().findExact('uuid', val);
                if(record > -1) {
                	record = mntent.getStore().getAt(record);
                	mntent.fireEvent('select', mntent,  record);
                }

            // no data volume?
            } else {
                Ext.getCmp(this.getId()+'-data-root-warn').show();
            }

			// No-password warning?
			if(r.warnnopassword) {
				this.findById(this.getId()+'-nopw').show();
			} else {
				this.findById(this.getId()+'-nopw').hide();
			}

			// Disable other tabs?
			var checked = this.findFormField("enable").checked;
			var tabs = [
				this.ownerCt.find('title','Databases')[0],
				this.ownerCt.find('title','Global Privileges')[0],
				this.ownerCt.find('title','Wildcard Privileges')[0]
			];

			if(checked && !r.warnnopassword) {
				Ext.each(tabs,function(t){t.enable();});
			} else {
				Ext.each(tabs,function(t){t.disable();});
			}

			// Reset various form fields
			var f = ["curpassword","password","password2"];
			for(var i = 0; i < f.length; i++) {
				var field = this.findFormField(f[i]);
				field.setValue("");
				field.allowBlank = true;
			}
			this.findFormField("force").setValue('false');

			// Hide "launch" button if phpMyAdmin is not enabled
			if(this.findFormField("enable-phpmyadmin").checked) {
				Ext.getCmp(this.getId()+"-launchmyadmin").show();
			} else {
				Ext.getCmp(this.getId()+"-launchmyadmin").hide();
			}

			// phpMyAdmin status
			if(r.phpmyadmininstalled) {
				Ext.getCmp(this.getId()+"-myadmininstall").hide();
				Ext.getCmp(this.getId()+"-myadmin").show();
			} else {
				Ext.getCmp(this.getId()+"-myadmininstall").show();
				Ext.getCmp(this.getId()+"-myadmin").hide();
			}

			// Hide all phpmyadmin fieldsets if service is not enabled
			if(!checked) {
				Ext.getCmp(this.getId()+"-myadmininstall").hide();
				Ext.getCmp(this.getId()+"-myadmin").hide();
			}
						
			// Update fields
			this._updateFormFields();
			
			// Reset validation
			OMV.Module.Services.MySQL.superclass.isValid.call(this);

		});
	},

	/* Overridden to refresh loaded data instead of original form fields */
	reset : function(){
		this.doLoad();
	},

	/* Overridden to provide extra validation */
	isValid : function() {

		var valid = OMV.Module.Services.MySQL.superclass.isValid.call(this);

		if (valid) {

			var p0 = this.findFormField("curpassword");
			var p1 = this.findFormField("password");
			var p2 = this.findFormField("password2");

			// Forcing a password change requires a password
			if(this.findFormField("force").getValue() && p1.getValue() == '') {

				var msg = "You must supply a new password.";

				this.markInvalid([
				                  { id: p1.id, msg: msg },
				                  { id: p2.id, msg: msg }
				                  ]);
				OMV.MessageBox.error(null, msg);
				return false;

			// If password1 exists, password2 must match
			} else if (p1.getValue() != p2.getValue()) {
				var msg = "Passwords don't match";
				this.markInvalid([
				                  { id: p1.id, msg: msg },
				                  { id: p2.id, msg: msg }
				                  ]);
				OMV.MessageBox.error(null, msg);
				return false;

			// If the no password warning is visible, no root
			// pw is set. One of current or password1 must be set
			} else if(!this.findById(this.getId()+'-nopw').hidden) {

				if(p0.getValue() + p1.getValue() == '') {

					OMV.MessageBox.error(null,this.findById(this.getId()+'-nopw').getEl().dom.
						childNodes[0].childNodes[0].innerHTML);

					return false;
				}
			}

		}

		return valid;

	},

	/** Remove (uninstall) phpMyAdmin */
	_removeMyAdmin: function() {
		OMV.MessageBox.wait(null, "Removing phpMyAdmin ...");
		OMV.Ajax.request(this.doReload, this, "mysql", "removeMyAdmin", []);
	},

	/** Install phpMyAdmin */
	_installMyAdmin: function() {
		var wnd = new OMV.ExecCmdDialog({
			title: "Install phpMyAdmin...",
			rpcService: "mysql",
			rpcMethod: "installMyAdmin",
			hideStart: true,
			hideStop: true,
			killCmdBeforeDestroy: false,
			listeners: {
				finish: function(wnd, response) {
					wnd.appendValue("\nDone ...");
					wnd.setButtonDisabled("close", false);
				},
				exception: function(wnd, error) {
					OMV.MessageBox.error(null, error);
					wnd.setButtonDisabled("close", false);
				},
				close: function() {
					this.doReload();
				},
				scope: this

			}
		});
		wnd.setButtonDisabled("close", true);
		wnd.show();
		wnd.start();
	},

	getFormItems : function() {
		return [{
			xtype: "fieldset",
			title: "General settings",
			defaults: { labelSeparator: "" },
			items: [{
				xtype: "checkbox",
				name: "enable",
				fieldLabel: "Enable",
				checked: false,
				inputValue: 1,
				listeners: {
					check: this._updateFormFields,
					scope: this
				}
			},{
				xtype: "checkbox",
				name: "enable-networking",
				fieldLabel: "Enable Networking",
				checked: false,
				inputValue: 1,
				listeners: {
					check: this._updateNetFields,
					scope: this
				}
			},{
				xtype: "numberfield",
				name: "port",
				fieldLabel: "Port",
				vtype: "port",
				minValue: 0,
				maxValue: 65535,
				allowDecimals: false,
				allowNegative: false,
				allowBlank: false,
				value: 3306,
				plugins: [ OMV.form.plugins.FieldInfo ],
				infoText: "Port to listen on."
			},{
				xtype: "textfield",
				name: "bindaddress",
				fieldLabel: "Bind address",
				vtype: "IPv4Net",
				allowBlank: false,
				value: "0.0.0.0",
				plugins: [ OMV.form.plugins.FieldInfo ],
				infoText: "IP address to listen on. Use 0.0.0.0 for all host IPs."					
			},{
				xtype: "textfield",
				name: "extraoptions",
				fieldLabel: "Extra options",
				allowBlank: true,
				autoCreate: {
					tag: "textarea",
					autocomplete: "off",
					rows: "7",
					cols: "75"
				},
				plugins: [ OMV.form.plugins.FieldInfo ],
				infoText: "Extra options for [mysqld] section of MySQL configuration."
			}]
		},{
			xtype: "fieldset",
			title: "Data",
			items: [{
                xtype: "combo",
                name: "mntentref",
                hiddenName: "mntentref",
                fieldLabel: "SQL Data Volume",
                emptyText: "Select a volume ...",
                allowBlank: false,
                allowNone: false,
                value: 'none',
                width: 400,
                editable: false,
                triggerAction: "all",
                displayField: "description",
                valueField: "uuid",
				listeners : {
					select : function(sel,record) {

						if(sel.getValue() == 'none' && !record.get('datavol')) {
                			Ext.getCmp(this.getId()+'-data-root-warn').show();
						} else {
							Ext.getCmp(this.getId()+'-data-root-warn').hide();
						}
                        Ext.getCmp(this.getId()+'-data-root-label')
                        	.setText(record.get('datadir'));

					},
					scope: this
				},
                store: new OMV.data.Store({
                    remoteSort: false,
                    autoLoad: true,
                    proxy: new OMV.data.DataProxy("mysql", "getSQLVolCandidates"),
                    reader: new Ext.data.JsonReader({
                        idProperty: "uuid",
                        fields: [
                                 { name: "uuid" },
                                 { name: "datadir" },
                                 { name: "datavol" },
                                 { name: "description" }
                                 ]
                    }),
                    listeners: {
                    	load: function(s) {
                    		// Fire select
                    		var sel = this.findFormField('mntentref');
                            var record = s.getById(sel.getValue());
                            if(record) {
                            	sel.fireEvent('select', sel,  record);
                            }

                    		
                    	},
                    	scope: this
                    }
                })
            },{
                xtype: "hidden",
                name: "default-datadir",
				submitValue: false
			},{
                xtype: "hidden",
                name: "data-root"
            },{
                xtype: "label",
                id: this.getId() + "-data-root-label",
                fieldLabel: "SQL Data Root",
                text: "-"
            },{
                xtype: "label",
                id: this.getId() + "-data-root-warn",
				hideLabel: true,
                text: "Warning: Your MySQL data volume is set to use a non-data volume. This may result in loss of data in the event of a disk failure. It is recommmended that you change this before using MySQL. Once databases exist, you will be unable to choose the location of MySQL data through this interface."
			}]
		},{
			xtype: "fieldset",
			title: "Change MySQL root password",
			items: [{
				id: this.getId() + "-nopw",
				html: "<p>Warning: Either the MySQL root password "+
					"is not set, or it is not known to OpenMediaVault.</p><br /><p>(*) If "+
					"you have just installed this plugin and had not previously used MySQL, "+
					" you should leave Current password blank and "+
					"use the New password fields to set the MySQL root password.</p><br /><p>(*) "+
					"If you have an existing MySQL installation "+
					"simply tell OpenMediaVault what the current root password is by entering "+
					"it in the Current password field.</p><br /><p> </p>"
			},{
				xtype: "passwordfield",
				name: "curpassword",
				id: this.getId()+"-curpassword",
				inputValue: "",
				fieldLabel: "Current password",
				allowBlank: true,
				listeners: {
					scope: this,
					blur: function(b) {

						var nopw = !(this.findById(this.getId()+'-nopw').hidden);
						this.findFormField("password").allowBlank = nopw || (b.getValue().length == 0);
						this.findFormField("password2").allowBlank = nopw ||(b.getValue().length == 0);
						OMV.Module.Services.MySQL.superclass.isValid.call(this);
					}
				}
			},{
				xtype: "passwordfield",
				name: "password",
				fieldLabel: "New password",
				inputValue: "",
				allowBlank: true
			},{
				xtype: "passwordfield",
				name: "password2",
				submitValue: false,
				inputValue: "",
				fieldLabel: "Confirm new password",
				allowBlank: true
            },{
                xtype: "button",
                text: "Generate",
				id: this.getId() + "-genpw",
                fieldLabel: " ",
                labelSeparator: "",
                handler: function(b) {
                    var pws = ['password','password2'];
                    var newPW = OMV.Module.Services.MySQL.genPassword();
                    Ext.each(pws, function(p) {
                        var pw = b.ownerCt.find('name', p)[0];
                        pw.setValue(newPW);
                        if(pw.getEl().dom.type == "password")
                            pw.onTriggerClick();
                    });
                }
			},{
				xtype: "checkbox",
				name: "force",
				fieldLabel: " ",
				inputValue: 1,
				boxLabel: "Force password reset",
				plugins: [ OMV.form.plugins.FieldInfo ],
				infoText: "This will cause a restart of MySQL and should only be required in rare cases."
			}]
		},{
			xtype: "fieldset",
			title: "phpMyAdmin",
			id: this.getId()+"-myadmininstall",
			defaults: { labelSeparator: "" },
			items: [{
				xtype: "button",
				text: "Install phpMyAdmin",
				submitValue: false,
				fieldLabel: " ",
				scope: this,
				handler: this._installMyAdmin
			}]
		},{
			xtype: "fieldset",
			title: "phpMyAdmin",
			hidden: true,
			id: this.getId()+"-myadmin",
			defaults: { labelSeparator: "" },
			items: [{
				xtype: "checkbox",
				name: "enable-phpmyadmin",
				fieldLabel: "Enable",
				checked: false,
				inputValue: 1,
				plugins: [ OMV.form.plugins.FieldInfo ],
				infoText: "Enable phpMyAdmin web interface."
			},{
				xtype: "button",
				fieldLabel: " ",
				id: this.getId()+"-launchmyadmin",
				text: "Launch phpMyAdmin",
                                plugins: [ OMV.form.plugins.FieldInfo ],
                                infoText: "Log in with database level (MySQL) credentials.",
				handler: function() {
					window.open("/phpmyadmin/");
				}
			},{
				xtype: "button",
				text: "Remove phpMyAdmin",
				submitValue: "false",
				fieldLabel: " ",
				scope: this,
				handler: this._removeMyAdmin

			}]
		},{
			xtype: 'fieldset',
			title: 'Donate',
			items: [{
				html: 'I develop plugins for OpenMediaVault in my spare time, free of charge. If you find this plugin useful, I ask that you please donate.'
			},{
				html: "<div style='margin:10px'><a href='https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PPYY2F9ST2BAY' target=_blank><img src='images/donate-btn.gif' /></a></div>"
			}]
		}]
	},

	/**
	 * Private function to update the states of various form fields.
	 */
	_updateFormFields : function() {

		var checked = this.findFormField("enable").checked;
		var formItems = this.getFormItems();
		var fields = [];
		for(var a = 0; a < formItems.length; a++) {
			for(var b = 0; b < formItems[a].items.length; b++) {
				if(!formItems[a].items[b].name || formItems[a].items[b].name == "enable") continue;
				if(formItems[a].items[b].xtype == "label") continue;
				if(formItems[a].items[b].xtype == "button") continue;
				fields[fields.length] = formItems[a].items[b].name;
			}
		}
		for (var i = 0; i < fields.length; i++) {
			this.findFormField(fields[i]).setDisabled(!checked);
		}
		if(checked) this._updateNetFields();
	},

	/**
	 * Private function to update the port field
	 */
	_updateNetFields : function() {
		this.findFormField('port').setDisabled(!this.findFormField("enable-networking").checked);
		this.findFormField('bindaddress').setDisabled(!this.findFormField("enable-networking").checked);
	}

});

/**
 * 
 * Database list panel - second tab
 * 
 */
OMV.Module.Services.MySQL.DatabasesGridPanel = function(config) {

	var initialConfig = {
			disabled: true,
			hideRefresh: false,
			hidePagingToolbar: true,
			colModel: new Ext.grid.ColumnModel({
				columns: [{
					header: "Database",
					sortable: true,
					dataIndex: "Database"
				},{
					header: "Size",
					sortable: true,
					dataIndex: "size",
					renderer: function(v) { return v + ' MB'; }
				}]
			})
	};
	Ext.apply(initialConfig, config);
	OMV.Module.Services.MySQL.DatabasesGridPanel.superclass.constructor.call(
			this, initialConfig);
};

Ext.extend(OMV.Module.Services.MySQL.DatabasesGridPanel, OMV.grid.TBarGridPanel, {

	initComponent : function() {
		this.store = new OMV.data.Store({
			autoLoad: false,
			remoteSort: false,
			proxy: new OMV.data.DataProxy("mysql", "getDatabases"),
			reader: new Ext.data.JsonReader({
				idProperty: "Database",
				totalProperty: "total",
				root: "data",
				fields: [
				         { name: "Database" },
				         { name: "size" }
				         ]
			})
		});
		OMV.Module.Services.MySQL.DatabasesGridPanel.superclass.initComponent.
		apply(this, arguments);
	},

	// (Re)Load when this tab is shown
	listeners : {
		show: function() {
			this.doLoad();
		}
	},

	initToolbar : function() {

		var tbar = OMV.Module.Services.MySQL.DatabasesGridPanel.superclass.initToolbar.apply(this);

		// Add database privileges button
		tbar.insert(2, {
			id: this.getId() + "-privileges",
			xtype: "button",
			text: "Privileges",
			icon: "images/privileges.gif",
			handler: this.cbPrivilegesBtnHdl.createDelegate(this),
			disabled : true
		});

		return tbar;

	},

	// Add database
	cbAddBtnHdl : function() {
		var wnd = new OMV.Module.Services.MySQL.DatabaseDialog({
			uuid: OMV.UUID_UNDEFINED,
			listeners: {
				submit: function() {
					this.doReload();
				},
				scope: this
			}
		});
		wnd.show();
	},

	// Edit database
	cbEditBtnHdl : function() {
		var selModel = this.getSelectionModel();
		var record = selModel.getSelected();
		var wnd = new OMV.Module.Services.MySQL.DatabaseDialog({
			uuid: record.get("Database"),
			listeners: {
				submit: function() {
					this.doReload();
				},
				scope: this
			}
		});
		wnd.show();
	},

	// Delete database
	doDeletion : function(record) {
		OMV.Ajax.request(this.cbDeletionHdl, this, "mysql", "removeDatabase",
				[ record.get("Database") ]);
	},

	// Create and / or show database privileges tab
	cbPrivilegesBtnHdl : function() {

		var db = this.getSelectionModel().getSelected().get("Database");
		// unique id per database
		var tabid = "OMV-Module-Services-MySQL-PrivilegesGridPaneldbpriv-tab-" + db;
		var ppanel = this.findParentByType("tabpanel");

		// Tab exists. Just activate it
		if(ppanel.items.get(tabid)) {
			ppanel.activate(tabid);
			return;
		}

		// Instance of privileges panel
		var privPanel = new OMV.Module.Services.MySQL.PrivilegesGridPanel({
			title: "Privileges for `" + db + "`",
			closable: true,
			disabled: false,
			id: tabid,
			database: db,
			hideAdd: true,
			colModel: new Ext.grid.ColumnModel({
				columns: [{
					header: "User",
					sortable: true,
					dataIndex: "user",
					renderer: OMV.Module.Services.MySQL.privRenderer
				},{
					header: "Host",
					sortable: true,
					dataIndex: "host",
					renderer: OMV.Module.Services.MySQL.privRenderer
				},{
					header: "Type",
					sortable: true,
					dataIndex: "type",
					renderer: function(v) {
						if(v!='Database Specific')
							return '<b>'+v+'</b>';
						return v;
					}
				},{
					header: "Privileges",
					sortable: true,
					dataIndex: "privileges"
				},{
					header: "Grant",
					sortable: true,
					dataIndex: "grant"
				}]
			}),
			initToolbar : function() {
				var tpanel = this;
				var tbar = OMV.Module.Services.MySQL.PrivilegesGridPanel.superclass.initToolbar.apply(this);
				
				// Add button on database privilege panels have different different options
				tbar.insert(0, {
					id: this.getId() + "-addSplit",
					xtype: "button",
					text: "Add",
					icon: "images/add.png",
					scope: this,
					menu : [{
						text: 'Add new privilege entry',
						handler: function() { 
							tpanel.cbAddBtnHdl();
						}
					},{
						text: "Add globally defined privilege entry to '"+this.database+"'",
						handler: function() {
							tpanel.cbAddBtnHdl(true);
						}
					}]
				});

				return tbar;
			}

		});

		ppanel.add(privPanel);
		ppanel.activate(tabid);

	},

	// Update buttons when item selection changes
	cbSelectionChangeHdl : function(model) {
		
		OMV.Module.Services.MySQL.DatabasesGridPanel.superclass.cbSelectionChangeHdl.apply(this, arguments);
		
		// Process additional buttons
		var records = model.getSelections();
		var tbarPrivilegesCtrl = this.getTopToolbar().findById(this.getId() + "-privileges");
		if (records.length <= 0) {
			tbarPrivilegesCtrl.disable();
		} else if (records.length == 1) {
			tbarPrivilegesCtrl.enable();
		} else {
			tbarPrivilegesCtrl.disable();
		}
	}



});

/**
 * Privileges panel class for Global or Database privileges
 */
OMV.Module.Services.MySQL.PrivilegesGridPanel = function(config) {

	var initialConfig = {
			disabled: true,
			hideRefresh: false,
			hidePagingToolbar: true,

			colModel: new Ext.grid.ColumnModel({
				columns: [{
					header: "User",
					sortable: true,
					dataIndex: "user",
					renderer: OMV.Module.Services.MySQL.privRenderer
				},{
					header: "Host",
					sortable: true,
					dataIndex: "host",
					renderer: OMV.Module.Services.MySQL.privRenderer
				},{
					header: "Password",
					sortable: true,
					dataIndex: "password",
					renderer: function(v) {
						if(!v) return '<span style="color: #f00">No</span>';
						return 'Yes';
					}
				},{
					header: "Privileges",
					sortable: true,
					dataIndex: "privileges"
				},{
					header: "Grant",
					sortable: true,
					dataIndex: "grant"
				}]
			})
	};
	Ext.apply(initialConfig, config);
	OMV.Module.Services.MySQL.PrivilegesGridPanel.superclass.constructor.call(
			this, initialConfig);
};

Ext.extend(OMV.Module.Services.MySQL.PrivilegesGridPanel, OMV.grid.TBarGridPanel, {

	initComponent : function() {
		if(this.database) {
			params = [ this.database ];
		} else {
			this.database = String('');
			params = null;
		}
		this.store = new OMV.data.Store({
			autoLoad: false,
			remoteSort: false,
			proxy: new OMV.data.DataProxy("mysql", "getPrivileges", params),
			reader: new Ext.data.JsonReader({
				idProperty: "uuid",
				totalProperty: "total",
				root: "data",
				fields: [
				         { name: "uuid" },
				         { name: "user" },
				         { name: "host" },
				         { name: "password" },
				         { name: "type" },
				         { name: "db" },
				         { name: "privileges" },
				         { name: "grant" }
				         ]
			})
		});
		OMV.Module.Services.MySQL.PrivilegesGridPanel.superclass.initComponent.
		apply(this, arguments);
	},

	// (Re)load data when this tab is shown
	listeners : {
		show: function() {
			this.doLoad();
		}
	},

	initToolbar : function() {
		return OMV.Module.Services.MySQL.PrivilegesGridPanel.superclass.initToolbar.apply(this);
	},

	// Add new privilege entry
	cbAddBtnHdl : function(exst) {

		var wnd = new OMV.Module.Services.MySQL.PrivilegeDialog({
			uuid: OMV.UUID_UNDEFINED,
			database: this.database,
			wildcard: this.database == '%',
			addExistingUser: (exst === true),
			listeners: {
				submit: function() {
					this.doReload();
				},
				scope: this
			}
		});
		wnd.show();
	},

	// Edit privilege entry
	cbEditBtnHdl : function() {

		var selModel = this.getSelectionModel();
		var record = selModel.getSelected();

		// Can't edit global permissions from 
		// database specific tab
		if(this.database && this.database != '%' && record.get('type') != 'Database Specific')
			return;
		
		var wnd = new OMV.Module.Services.MySQL.PrivilegeDialog({
			uuid: record.get("uuid"),
			user: record.get("user"),
			host: record.get("host"),
			_mode: 'edit',
			wildcard: (this.database == '%'),
			database: (this.database == '%' ? record.get('db') : this.database),
			listeners: {
				submit: function() {
					this.doReload();
				},
				scope: this
			}
		});
		wnd.show();
	},

	// Remove privilege entry
	doDeletion : function(record) {

		// Database specific entry
		if(record.get("type") == 'Database Specific') {

			OMV.Ajax.request(this.cbDeletionHdl, this, "mysql", "removePrivilege",
				[ record.get("user") , record.get("host"), true, this.database, 0]);
			return;
		} 

		// Global entries can contain database wildcards
		OMV.Ajax.request(this.cbDeletionHdl, this, "mysql", "removePrivilege",
				[ record.get("user") , record.get("host"), record.get("db") != '',	
					record.get("db"), (this.database == '%' ? 1 : 0)]);
	},

	// Update buttons on selection change
	cbSelectionChangeHdl : function(model) {
		
		OMV.Module.Services.MySQL.PrivilegesGridPanel.superclass.cbSelectionChangeHdl.apply(this, arguments);
		
		// Process additional buttons
		var records = model.getSelections();
		var btnEdit = this.getTopToolbar().findById(this.getId() + "-edit");
		var btnDelete = this.getTopToolbar().findById(this.getId() + "-delete");
		
		if (records.length == 1) {

			// Can't edit global permissions from 
			// database specific tab			
			if(this.database && this.database != '%' && records[0].get('type') != 'Database Specific') {
				btnEdit.disable();
				btnDelete.disable();
			} else {
				btnEdit.enable();
				btnDelete.enable();
			}
		} else {
			btnEdit.disable();
			btnDelete.disable();
		}
	}



});

// Wildcard panel
OMV.Module.Services.MySQL.PrivilegesWildGridPanel =  function(config) {

    var initialConfig = {
			title: "Wildcard Privileges",
			id: "OMV-Module-Services-MySQL-Wildcards",
            disabled: true,
            hideRefresh: false,
			hideAdd: true,
			database: '%',
            hidePagingToolbar: true,

            colModel: new Ext.grid.ColumnModel({
                columns: [{
                    header: "User",
                    sortable: true,
                    dataIndex: "user",
                    renderer: OMV.Module.Services.MySQL.privRenderer
                },{
                    header: "Host",
                    sortable: true,
                    dataIndex: "host",
                    renderer: OMV.Module.Services.MySQL.privRenderer
                },{
                    header: "Database Wildcard",
                    sortable: true,
                    dataIndex: 'db'
                },{
                    header: "Privileges",
                    sortable: true,
                    dataIndex: "privileges"
                },{
                    header: "Grant",
                    sortable: true,
                    dataIndex: "grant"
                }]
            }),

            initToolbar : function() {
                var tpanel = this;
                var tbar = OMV.Module.Services.MySQL.PrivilegesGridPanel.superclass.initToolbar.apply(this);

                // Add button on database privilege panels have different different options
                tbar.insert(0, {
                    id: this.getId() + "-addSplit",
                    xtype: "button",
                    text: "Add",
                    icon: "images/add.png",
                    scope: this,
                    menu : [{
                        text: 'Add new privilege entry',
                        handler: function() {
                            tpanel.cbAddBtnHdl();
                        }
                    },{
                        text: "Create a wildcard privilege entry from a global privilege",
                        handler: function() {
                            tpanel.cbAddBtnHdl(true);
                        }
                    }]
                });

                return tbar;
            }

    };
    Ext.apply(initialConfig, config);
    OMV.Module.Services.MySQL.PrivilegesGridPanel.superclass.constructor.call(
            this, initialConfig);
}
Ext.extend(OMV.Module.Services.MySQL.PrivilegesWildGridPanel, OMV.Module.Services.MySQL.PrivilegesGridPanel);



// Register our 4 panels with OMV.NavigationPanelMgr
OMV.NavigationPanelMgr.registerPanel("services", "mysql", {
	cls: OMV.Module.Services.MySQL,
	position: 10,
	title: "Settings"
});
OMV.NavigationPanelMgr.registerPanel("services", "mysql", {
	cls: OMV.Module.Services.MySQL.DatabasesGridPanel,
	position : 20,
	title : "Databases"
});

OMV.NavigationPanelMgr.registerPanel("services", "mysql", {
	cls: OMV.Module.Services.MySQL.PrivilegesGridPanel,
	position : 30,
	title : "Global Privileges"
});

OMV.NavigationPanelMgr.registerPanel("services", "mysql", {
    cls: OMV.Module.Services.MySQL.PrivilegesWildGridPanel,
    position : 40,
    title : "Wildcard Privileges"
});

/*
 * Dialogs follow..
 */

/**
 * Add / edit database dialog
 */
OMV.Module.Services.MySQL.DatabaseDialog = function(config) {
	var initialConfig = {
			rpcService: "mysql",
			rpcSetMethod: "setDatabase",
			rpcGetMethod: "getDatabase",
			title: ((config.uuid == OMV.UUID_UNDEFINED) ? "Add" : "Edit") +
			" Database",
			autoHeight: true,
			width: 450
	};
	Ext.apply(initialConfig, config);
	OMV.Module.Services.MySQL.DatabaseDialog.superclass.constructor.call(
			this, initialConfig);
};

Ext.extend(OMV.Module.Services.MySQL.DatabaseDialog, OMV.CfgObjectDialog, {
	initComponent : function() {
		OMV.Module.Services.MySQL.DatabaseDialog.superclass.initComponent.
		apply(this, arguments);
		this.on("load", this._updateFormFields, this);
	},

	getFormConfig : function() {
		return {autoHeight: true};
	},

	getFormItems : function() {
		return [{
			xtype: "textfield",
			name: "name",
			itemId: "name",
			fieldLabel: "Name",
			plugins: [ OMV.form.plugins.FieldInfo ],
			infoText: "Database name.",
			allowBlank: false
		},{
			xtype: "combo",
			name: "collation",
			hiddenName: "collation",
			fieldLabel: "Collation",
			emptyText: "Select collation ...",
			allowBlank: true,
			allowNone: true,
			width: 400,
			editable: false,
			triggerAction: "all",
			displayField: "description",
			valueField: "collation",
			store: new OMV.data.Store({
				remoteSort: false,
				proxy: new OMV.data.DataProxy("mysql", "getCollations"),
				reader: new Ext.data.JsonReader({
					idProperty: "collation",
					fields: [
					         { name: "collation" },
					         { name: "description" }
					         ]
				})
			})
		},{
			xtype: "hidden",
			name: "exists",
			value: "0"
		}]
	},

	/**
	 * Private function to update the states of various form fields.
	 */
	_updateFormFields : function() {
		if ((this.uuid !== OMV.UUID_UNDEFINED)) {
			this.findFormField("name").setReadOnly(true);
			this.findFormField("exists").setValue("1");
		}
	}

});

/**
 * Renderer for privilege columns
 */
OMV.Module.Services.MySQL.privRenderer = function(val) {
	switch(val) {
	case '':
	case null:
		return '<span style="color: #f00">Any</span>';
	default:
		return val;
	}
}

/**
 * Add / edit privilege entry dialog
 */
OMV.Module.Services.MySQL.PrivilegeDialog = function(config) {
	var initialConfig = {
			rpcService: "mysql",
			rpcSetMethod: "setPrivilege",
			rpcGetMethod: "getPrivilege",
			title: ((config.uuid == OMV.UUID_UNDEFINED) ? "Add" : "Edit") +
			" Privilege" + (config.database ? " on " + config.database : "") +
			(config.user || config.host ? " for " + config.user + "@" + config.host : ""),
			autoHeight: false,
			border: false,
			width: 450,
			height: 490
	};
	Ext.apply(initialConfig, config);
	OMV.Module.Services.MySQL.PrivilegeDialog.superclass.constructor.call(
			this, initialConfig);
};

Ext.extend(OMV.Module.Services.MySQL.PrivilegeDialog, OMV.CfgObjectDialog, {

	initComponent : function() {

		OMV.Module.Services.MySQL.PrivilegeDialog.superclass.initComponent.apply(this, arguments);

		// Hide general tab on DB user edits. There's nothing to change there
		this.on("beforeshow", function(){
			if(this.database && this._mode == 'edit') {
				var tp = Ext.getCmp("OMV-Module-Services-MySQL-PrivDialogTabPanel");
				tp.activate("OMV-Module-Services-MySQL-PrivDialogTabPanel-Priv");
				tp.hideTabStripItem("OMV-Module-Services-MySQL-PrivDialogTabPanel-General");
			}

		});
	},

	// Applied to various components.
	_bodyStyle: 'background-color: #efefef; color: #000; padding: 4px; ',


	// Override doLoad since we pass multiple values
	// and escape db names
	doLoad : function() {

		// Display waiting dialog
		OMV.MessageBox.wait(null, "Loading ...");

		// Execute RPC
		OMV.Ajax.request(this.cbLoadHdl, this, this.rpcService,
				this.rpcGetMethod, [ this.user, this.host, this.database, this.wildcard ]);
	},

	// Override isValid to provide extra validation
	isValid : function() {

		var valid = OMV.Module.Services.MySQL.PrivilegeDialog.superclass.isValid.call(this);

		if (valid) {

			var values = this.getValues();

			var p1 = null;
			var p2 = null;

			// If it's an existing user, check for change password
			if(this._mode == 'edit') {
				p1 = this.findFormField("newpassword");
				p2 = this.findFormField("newpassword2");
				if(this.findFormField("settoblank").checked) p1 = p2;
			} else {
				p1 = this.findFormField("password");
				p2 = this.findFormField("password2");
			}

			if (p1 && p2 && p1.getValue() != p2.getValue()) {
				var msg = "Passwords don't match";
				this.markInvalid([
				                  { id: p1.id, msg: msg },
				                  { id: p2.id, msg: msg }
				                  ]);
				OMV.MessageBox.error(null, msg);
				valid = false;

				// Check that at least SOME permissions are set
				// when adding an existing permission entry to a db
			} else if(this.database) {

				var cbs = Ext.getCmp("OMV-Module-Services-MySQL-PrivDialogTabPanel-Priv").
				findByType('checkbox');
				var privSelected = false;
				for(var i = 0; i < cbs.length; i++) {
					if(cbs[i].checked) {
						privSelected = true;
						break;
					}
				}
				if(!privSelected)
					OMV.MessageBox.error(null, "Select at least one privilege option on the Database Permissions tab.");

				valid = privSelected;

			}
		}
		return valid;
	},

	getFormItems: function() {

		var panel = this;

		// Global privileges panel definition
		var gprivs = [{
			title: "Data Permissions",
			items : ["SELECT", "UPDATE", "INSERT", "DELETE", "FILE"]
		},{
			title: "Structure Permissions",
			items: ["CREATE", "ALTER","INDEX", "DROP", "SHOW VIEW", "CREATE ROUTINE",
			        "ALTER ROUTINE", "EXECUTE", "CREATE VIEW", "EVENT", "TRIGGER", "CREATE TEMPORARY TABLES" ]
		},{
			title: "Administrative Permissions",
			items : ["GRANT","SUPER", "PROCESS", "RELOAD", "SHUTDOWN", "LOCK TABLES",
			         "REFERENCES", "CREATE USER", "SHOW DATABASES", "REPLICATION CLIENT", "REPLICATION SLAVE" ]
		}];

		// Database privileges
		var dbprivs = OMV.Module.Services.MySQL.clone(gprivs);
		dbprivs[0].items.pop();
		dbprivs[2].items = ["GRANT","LOCK TABLES","REFERENCES"];

		/**
		 * Dynamically generate privilege tab configuration
		 */
		var privTab = {
			xtype: "panel",
			layout: "form",
			title: "Global Permissions",
			id: "OMV-Module-Services-MySQL-PrivDialogTabPanel-Priv",
			border: false,
			bodyStyle: this._bodyStyle,
			autoHeight: true,
			items: [{
				xtype: "hidden",
				name: "wildcard",
				value: (this.wildcard ? 1 : 0)
			},{
				xtype: "buttongroup",
				labelSeparator: "" ,
				border: false,
				fieldLabel: " ",
				shadow: false,
				items: [{
					xtype: "button",
					text: "Check all",
					submitValue: false,
					handler: function(b) {
						var cbs = b.ownerCt.ownerCt.findByType('checkbox');
						Ext.each(cbs, function(cb) {
							cb.setValue('true');
						});
					}
				},{
					xtype: "button",
					text: "Uncheck all",
					submitValue: false,
					handler: function(b) {
						var cbs = b.ownerCt.ownerCt.findByType('checkbox');
						Ext.each(cbs, function(cb) {
							cb.setValue('false');
						});
					}
				}]
			}]
		};
		// Database privileges panel definition
		var privlist = null;
		if(this.database) {

			privTab.title = "Database Permissions";
			privlist = dbprivs;

		} else {

			privTab.title = "Global Permissions";
			privlist = gprivs;

		}
		
		// Note: Ext.each() seems to break things if used here
		for(var a = 0; a < privlist.length; a++) {

			// Each privilege category creates a fieldset
			var p = {
					xtype: "fieldset",
					layout: "table",
					layoutConfig: {
						columns: 3,
						tableAttrs: { style: { width: '100%' } }
					},
					title: privlist[a].title,
					items: []
			};

			// Each item under this category is added as a checkbox
			for(var b = 0; b < privlist[a].items.length; b++) {
				var cb = privlist[a].items[b];
				p.items[p.items.length] = {
						hideLabel: true,
						xtype: "checkbox",
						name: "PRIV_"+cb,
						id: "PRIV_"+cb,
						boxLabel: cb,
						inputValue: 1
				}
			}

			privTab.items[privTab.items.length] = p;

		}

		return [{
			xtype: "tabpanel",
			layoutConfig: { padding: '4', border: 0 },
			hideBorders: true,
			plain: true,
			shadow: false,
			unstyled: true,
			id: "OMV-Module-Services-MySQL-PrivDialogTabPanel",
			activeTab: 0,
			items: [{
				xtype: "panel",
				layout: "form",
				title: "General",
				id: "OMV-Module-Services-MySQL-PrivDialogTabPanel-General",
				autoHeight: true,
				bodyStyle: this._bodyStyle,
				items: [{
					xtype: "hidden",
					name: "exUser",
					itemId: "exUser",
					allowBlank: true,
					value: this.user
				},{
					xtype: "hidden",
					name: "exHost",
					itemId: "exHost",
					allowBlank: true,
					value: this.host
				},{
					xtype: "hidden",
					name: "exDb",
					allowBlank: true,
					value: this.database
				},{
					xtype: "fieldset",
					hidden: (!this.addExistingUser),
					title: "User and Host",
					items: [{
						xtype: "combo",
						hideLabel: true,
						name: "existingUser",
						submitValue: false,
						emptyText: "Select ...",
						allowBlank: (!this.addExistingUser),
						allowNone: (!this.addExistingUser),
						editable: false,
						width: 330,
						triggerAction: "all",
						displayField: "description",
						valueField: "description",
						store: new OMV.data.Store({
							remoteSort: false,
							proxy: new OMV.data.DataProxy("mysql", "getUsers"),
							reader: new Ext.data.JsonReader({
								idProperty: "uuid",
								fields: [
								         { name: "uuid" },
								         { name: "user" },
								         { name: "host" },
								         { name: "description" }
								         ]
							})
						}),
						listeners: {
							select: function(c, r) {
								panel.uuid = r.get('uuid');
								Ext.getCmp("OMV-Module-Services-MySQL-PrivDialogTabPanel-General").
								getComponent('exUser').setValue(r.get('user'));
								Ext.getCmp("OMV-Module-Services-MySQL-PrivDialogTabPanel-General").
								getComponent('exHost').setValue(r.get('host'));
							}
						}
					}]
				},{
					xtype: "fieldset",
					hidden: (this._mode == 'edit' || this.addExistingUser),
					title: "New User and Host",
					items: [{
						xtype: "textfield",
						name: "username",
						itemId: "username",
						vtype: "username",
						allowBlank: true,
						fieldLabel: "User name",
						plugins: [ OMV.form.plugins.FieldInfo ],
						infoText: "Leave blank to specify 'any' user."
					},{
						xtype: "passwordfield",
						allowBlank: true,
						name: "password",
						inputType: "password",
						fieldLabel: "Password"
					},{
						xtype: "passwordfield",
						name: "password2",
						itemId: "password2",
						allowBlank: true,
						submitValue: false,
						fieldLabel: "Confirm password"
					},{
						xtype: "button",
						text: "Generate",
						fieldLabel: " ",
						labelSeparator: "",
						handler: function(b) {
							var pws = ['password','password2'];
							var newPW = OMV.Module.Services.MySQL.genPassword();
							Ext.each(pws, function(p) {
								var pw = b.ownerCt.find('name', p)[0];
								pw.setValue(newPW);
								if(pw.getEl().dom.type == "password")
									pw.onTriggerClick();
							});
						}
					},{
						xtype: "textfield",
						name: "host",
						itemId: "host",
						plugins: [ OMV.form.plugins.FieldInfo ],
						infoText: "Host name or IP address. Set to '%' for 'any' host.",
						value: '%',
						allowBlank: (this._mode == 'edit' || this.addExistingUser),
						fieldLabel: "Host"
					}]
				},{
					// just for spacing..
					xtype: "label",
					text: " ",
					fieldLabel: " ",
					labelSeparator: "" ,
					hidden: (this._mode != 'edit')
				},{
					xtype: "label",
					fieldLabel: " ",
					labelSeparator: "" ,
					text: "'" + this.user + "'@'" + this.host+"'",
					hidden: (this._mode != 'edit')
				},{
					// just for spacing..
					xtype: "label",
					text: " ",
					fieldLabel: " ",
					labelSeparator: "" ,
					hidden: (this._mode != 'edit')
				},{
					xtype: "fieldset",
					title: "Change Password",
					hidden: (this._mode != 'edit' || this.database),
					items: [{
						xtype: "passwordfield",
						name: "newpassword",
						allowBlank: true,
						fieldLabel: "Password"
					},{
						xtype: "passwordfield",
						name: "newpassword2",
						submitValue: false,
						allowBlank: true,
						fieldLabel: "Confirm password"
                    },{
                        xtype: "button",
                        text: "Generate",
                        fieldLabel: " ",
                        labelSeparator: "",
                        handler: function(b) {
                            var pws = ['newpassword','newpassword2'];
                            var newPW = OMV.Module.Services.MySQL.genPassword();
                            Ext.each(pws, function(p) {
                                var pw = b.ownerCt.find('name', p)[0];
                                pw.setValue(newPW);
                                if(pw.getEl().dom.type == "password")
                                    pw.onTriggerClick();
                            });
                        }
					},{
						xtype : "checkbox",
						name : "settoblank",
						boxLabel : "Set to blank password"
					}]
				},{
					xtype: "fieldset",
					title: "Database Wildcard",
					hidden: !(this._mode != 'edit' && this.database == '%'),
					items: [{
						xtype: "textfield",
						fieldLabel: "Match",
						name: "dbmatch",
						allowBlank: !(this._mode != 'edit' && this.database == '%')
					},{
						html: "The _ and % wildcards are permitted when specifying database names. This means that if you want to use a _ character as part of a database name, you should specify it as \\_ to prevent the user from being able to access additional databases matching the wildcard pattern."
					}]
				},{
					xtype: "fieldset",
					title: "Database for user",
					hidden: (this._mode == 'edit' || this.database),
					items: [{
						name: "newUserDB",
						itemId: "newUserDB",
						xtype: "hidden",
						inputValue: "none"
					},{
						name: "newUserDBRadio",
						xtype: "radio",
						boxLabel : "None",
						hideLabel: true,
						inputValue: "none",
						checked: true,
						listeners: {
							check: function(b,checked) {
								if(b.checked) {
									var gdb = b.ownerCt.getComponent("grantdatabase");
									gdb.setDisabled(true);
									gdb.allowBlank = true;
									b.ownerCt.getComponent("newUserDB").setValue("none");
								}
							}
						}
					},{
						name: "newUserDBRadio",
						xtype: "radio",
						boxLabel : "Create database with same name and grant all privileges",
						hideLabel: true,
						inputValue: "grantnew",
						listeners: {
							check: function(b,checked) {
								if(b.checked) {
									var gdb = b.ownerCt.getComponent("grantdatabase");
									gdb.setDisabled(true);
									gdb.allowBlank = true;
									b.ownerCt.getComponent("newUserDB").setValue("grantnew");
								}

							}
						}
					},{
						name: "newUserDBRadio",
						xtype: "radio",
						hideLabel: true,
						boxLabel : "Grant all privileges on:",
						inputValue: "granton",
						listeners: {
							check: function(b,checked) {
								if(b.checked) {
									var gdb = b.ownerCt.getComponent("grantdatabase");
									gdb.setDisabled(false);
									gdb.allowBlank = false;
									b.ownerCt.getComponent("newUserDB").setValue("granton");
								}

							}
						}
					},{
						xtype: "combo",
						emptyText: "Select or enter database ...",
						itemId: "grantdatabase",
						name: "grantdatabase",
						disabled: true,
						allowBlank: true,
						allowNone: true,
						editable: true,
						triggerAction: "all",
						forceSelection: false,
						displayField: "Database",
						valueField: "escaped",
						width: 240,
						store: new OMV.data.Store({
							autoLoad: true,
							remoteSort: false,
							proxy: new OMV.data.DataProxy("mysql", "getDatabases"),
							reader: new Ext.data.JsonReader({
								idProperty: "Database",
								totalProperty: "total",
								root: "data",
								fields: [ { name: "Database" }, { name: "escaped" } ]
							})
						})
					},{
                    	html: "The _ and % wildcards are permitted when specifying database names. This means that if you want to use a _ character as part of a database name, you should specify it as \\_ to prevent the user from being able to access additional databases matching the wildcard pattern."

					}]
				}]
			},
			// generated above
			privTab
			]
		}]
	}
});

// Fix ExtJS bug where check is only fired once for radio buttons
Ext.override(Ext.form.Radio, {
	onClick : function(){
		if(this.el.dom.checked != this.checked){
			var els = this.el.up('form').select('input[name='+this.el.dom.name+']');
			els.each(function(el){
				if(el.dom.id == this.id){
					this.setValue(true);
				}else{
					Ext.getCmp(el.dom.id).setValue(false);    	        
				}
			}, this);
		}
	}
});

/**
 * Clone Function
 * @param {Object/Array} o Object or array to clone
 * @return {Object/Array} Deep clone of an object or an array
 * @author Ing. Jozef Saká.
 */
OMV.Module.Services.MySQL.clone = function(o) {
	if(!o || 'object' !== typeof o) {
		return o;
	}
	if('function' === typeof o.clone) {
		return o.clone();
	}
	var c = '[object Array]' === Object.prototype.toString.call(o) ? [] : {};
	var p, v;
	for(p in o) {
		if(o.hasOwnProperty(p)) {
			v = o[p];
			if(v && 'object' === typeof v) {
				c[p] = OMV.Module.Services.MySQL.clone(v);
			}
			else {
				c[p] = v;
			}
		}
	}
	return c;
}; // eo function clone  

/**
 * Generate password. Taken from phpMyAdmin
 */
OMV.Module.Services.MySQL.genPassword = function() {
	var pwchars = "abcdefhjmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWYXZ";
	var passwordlength = 16;
	var passwd = '';

	for ( i = 0; i < passwordlength; i++ ) {
		passwd += pwchars.charAt( Math.floor( Math.random() * pwchars.length ) )
	}
	return passwd;
};

